/*
 * Copyright (c) 2023-2025 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.common.mybatis.injector.methods;

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
import java.util.Arrays;
import java.util.stream.Collectors;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.elsfs.cloud.common.mybatis.ext.mybatis.extension.JoinWrapper;
import org.elsfs.cloud.common.util.lang.StringUtils;

/** 抽象连接方法实现 */
public abstract class AbstractJoinMethod extends AbstractMethod {
  public static final String LOGIC_DELETE_FIELD_NAME = "delete_flag";

  /** 逻辑删除字段名称 */
  private static final String JOIN_LOGIC_FIELD_NAME =
      JoinWrapper.TABLE_ALIAS + "." + LOGIC_DELETE_FIELD_NAME;

  /** SQL脚本 */
  private static final String SQL_SCRIPT = "<script>%s SELECT %s FROM %s %s %s %s\n</script>";

  /** 获取JOIN SQL方法 */
  protected static final String JOIN_SQL_METHOD = WRAPPER_DOT + "getJoinSqlSegment()";

  /**
   * 构造器
   *
   * @param methodName 方法名
   * @since 3.5.0
   */
  protected AbstractJoinMethod(String methodName) {
    super(methodName);
  }

  /**
   * SQL 查询所有表字段
   *
   * @param table 表信息
   * @param queryWrapper 是否为使用 queryWrapper 查询
   * @return sql 脚本
   */
  @Override
  protected String sqlSelectColumns(TableInfo table, boolean queryWrapper) {
    /* 假设存在用户自定义的 resultMap 映射返回 */
    String selectColumns = ASTERISK;
    if (table.getResultMap() == null || table.isAutoInitResultMap()) {
      /* 未设置 resultMap 或者 resultMap 是自动构建的,视为属于mp的规则范围内 */

      selectColumns =
          Arrays.stream(StringUtils.split(table.getAllSqlSelect(), ","))
              .map(field -> JoinWrapper.TABLE_ALIAS_PREFIX + field)
              .collect(Collectors.joining(JoinWrapper.FIELD_SEPARATOR));
    }

    if (!queryWrapper) {
      return selectColumns;
    }

    String unSafeParam = SqlScriptUtils.unSafeParam(Q_WRAPPER_SQL_SELECT);
    return SqlScriptUtils.convertChoose(
        String.format("%s != null and %s != null", WRAPPER, Q_WRAPPER_SQL_SELECT),
        SqlScriptUtils.convertChoose(
            String.format("%s", WRAPPER_DOT + "defaultSelectedFieldsCleared"),
            unSafeParam,
            selectColumns + JoinWrapper.FIELD_SEPARATOR + unSafeParam),
        selectColumns);
  }

  /**
   * EntityWrapper方式获取select where
   *
   * @param newLine 是否提到下一行
   * @param table 表信息
   * @return String
   */
  @Override
  protected String sqlWhereEntityWrapper(boolean newLine, TableInfo table) {
    if (table.isWithLogicDelete()) {
      final var sqlScriptStr =
          SqlScriptUtils.convertIf(
              table.getAllSqlWhere(true, true, true, WRAPPER_ENTITY_DOT),
              String.format("%s != null", WRAPPER_ENTITY),
              true);

      StringBuilder sqlScript =
          new StringBuilder(
              sqlScriptStr.replace("\"> AND ", "\"> AND " + JoinWrapper.TABLE_ALIAS_PREFIX));
      sqlScript
          .append(NEWLINE)
          .append(this.handleLogicDeleteSql(table.getLogicDeleteSql(true, true)))
          .append(NEWLINE);
      String normalSqlScript =
          SqlScriptUtils.convertIf(
                  String.format("AND ${%s}", WRAPPER_SQLSEGMENT),
                  String.format(
                      "%s != null and %s != '' and %s",
                      WRAPPER_SQLSEGMENT, WRAPPER_SQLSEGMENT, WRAPPER_NONEMPTYOFNORMAL),
                  true)
              + NEWLINE
              + SqlScriptUtils.convertIf(
                  String.format(" ${%s}", WRAPPER_SQLSEGMENT),
                  String.format(
                      "%s != null and %s != '' and %s",
                      WRAPPER_SQLSEGMENT, WRAPPER_SQLSEGMENT, WRAPPER_EMPTYOFNORMAL),
                  true);
      sqlScript.append(normalSqlScript);
      String sql =
          SqlScriptUtils.convertWhere(
              SqlScriptUtils.convertChoose(
                  String.format("%s != null", WRAPPER),
                  sqlScript.toString(),
                  this.handleLogicDeleteSql(table.getLogicDeleteSql(false, true))));
      return newLine ? NEWLINE + sql : sql;
    } else {
      StringBuilder sqlScript =
          new StringBuilder(
              SqlScriptUtils.convertIf(
                  table.getAllSqlWhere(false, true, true, WRAPPER_ENTITY_DOT),
                  String.format("%s != null", WRAPPER_ENTITY),
                  true));
      sqlScript
          .append(NEWLINE)
          .append(
              SqlScriptUtils.convertIf(
                  String.format(
                      SqlScriptUtils.convertIf(
                              " AND",
                              String.format(
                                  "%s and %s", WRAPPER_NONEMPTYOFENTITY, WRAPPER_NONEMPTYOFNORMAL),
                              false)
                          + " ${%s}",
                      WRAPPER_SQLSEGMENT),
                  String.format(
                      "%s != null and %s != '' and %s",
                      WRAPPER_SQLSEGMENT, WRAPPER_SQLSEGMENT, WRAPPER_NONEMPTYOFWHERE),
                  true));

      sqlScript = new StringBuilder(SqlScriptUtils.convertWhere(sqlScript.toString()));
      sqlScript
          .append(NEWLINE)
          .append(
              SqlScriptUtils.convertIf(
                  String.format(" ${%s}", WRAPPER_SQLSEGMENT),
                  String.format(
                      "%s != null and %s != '' and %s",
                      WRAPPER_SQLSEGMENT, WRAPPER_SQLSEGMENT, WRAPPER_EMPTYOFWHERE),
                  true));
      String sql =
          SqlScriptUtils.convertIf(
              sqlScript.toString(), String.format("%s != null", WRAPPER), true);
      return newLine ? NEWLINE + sql : sql;
    }
  }

  /**
   * 替换连表SQL中的逻辑删除字段名称
   *
   * @param sql SQL
   * @return 替换后的SQL
   */
  protected String handleLogicDeleteSql(String sql) {
    return sql.replace(LOGIC_DELETE_FIELD_NAME, JOIN_LOGIC_FIELD_NAME);
  }

  /**
   * 注入自定义 MappedStatement
   *
   * @param mapperClass mapper 接口
   * @param modelClass mapper 泛型
   * @param tableInfo 数据库表反射信息
   * @return MappedStatement
   */
  @Override
  public MappedStatement injectMappedStatement(
      Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
    String sql =
        String.format(
            SQL_SCRIPT,
            this.sqlFirst(),
            this.sqlSelectColumns(tableInfo, true),
            tableInfo.getTableName() + " AS " + JoinWrapper.TABLE_ALIAS,
            SqlScriptUtils.unSafeParam(JOIN_SQL_METHOD),
            this.sqlWhereEntityWrapper(true, tableInfo),
            this.sqlComment());

    SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
    return this.addSelectMappedStatementForTable(
        mapperClass, this.methodName, sqlSource, tableInfo);
  }
}
